/**
 * 
 */
package ro.dta.idbi.model;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.InvalidParameterException;
import java.util.EnumSet;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.ITransformationResult;
import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.interfaces.ui.ITransformationDialog;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Pattern class, used to describe a transformation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class Pattern {

	/**
	 * Name of the pattern
	 */
	private final String name;

	/**
	 * Description of the pattern
	 */
	private final String description;

	/**
	 * Command of the pattern
	 */
	private final String command;

	/**
	 * Integration phase during which the pattern is applied (can be CONFORMING,
	 * MERGING or IMPROVING)
	 */
	private final EnumSet<IntegrationOperation> operation;

	/**
	 * Class used to describe the pattern
	 */
	private final Class<?> patternClass;

	/**
	 * True if the pattern has an UI dialog associated with it, false otherwise
	 */
	private boolean containsUI;

	/**
	 * Java class of the UI dialog associated with the pattern
	 */
	private Class<?> uiClass;

	/**
	 * Pattern class constructor
	 * 
	 * @param cls
	 *            Class used to describe the pattern
	 * @param patternName
	 *            Name of the pattern
	 * @param operation
	 *            Integration phase during which the pattern is applied (can be
	 *            CONFORMING, MERGING or IMPROVING)
	 */
	public Pattern(final Class<?> cls, final String patternName,
			final EnumSet<IntegrationOperation> operation) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
		if (Modifier.isAbstract(cls.getModifiers())
				|| (!cls.getSuperclass().equals(AbstractPattern.class) && !cls.getSuperclass().equals(AbstractPrimitivePattern.class))) {
			throw new InvalidParameterException(
					"Class must be a subclass of TransformationPattern.");
		}
		this.patternClass = cls;
		this.name = patternName;
		this.operation = operation;

		Field field = cls.getField("COMMAND");
		this.command = field.get(null).toString();

		field = cls.getField("DESCRIPTION");
		this.description = field.get(null).toString();

		final String uiPath = "ro.dta.idbi.ui.patterns." + cls.getSimpleName() + "Dialog";
		try {
			uiClass = Class.forName(uiPath);
			containsUI = true;
		} catch (ClassNotFoundException e) {
			containsUI = false;
		}
	}

	/**
	 * Check if the pattern can be applied over an array of objects
	 * 
	 * @param schema
	 *            Schema in which to check the validity of the array of objects
	 * @param selObjs
	 *            Array of objects to check for
	 * @return True if pattern can be applied, false otherwise
	 */
	public boolean isValid(final Schema schema, final Object[] selObjs) {
		boolean res = false;
		if (containsUI) {
			try {
				final Method method = uiClass.getMethod("isValid", Schema.class,
						Object[].class);
				final Boolean result = (Boolean) method.invoke(null,
						new Object[] { schema, selObjs });
				res = result;
			} catch (Exception e) {
				// Method does not exist, return false
				res = false;
			}
		}

		return res;
	}

	/**
	 * Get the name of the pattern
	 * 
	 * @return Name of the pattern
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return The description of the pattern
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * @return The command associated with the pattern
	 */
	public String getCommand() {
		return command;
	}

	/**
	 * @return Integration phase during which the pattern is applied (can be
	 *         CONFORMING, MERGING or IMPROVING)
	 */
	public EnumSet<IntegrationOperation> getOperation() {
		return operation;
	}

	/**
	 * @return Java class associated with the pattern
	 */
	public Class<?> getPatternClass() {
		return patternClass;
	}

	/**
	 * @return True if pattern has an UI dialog associated with it, false
	 *         otherwise
	 */
	public boolean hasUI() {
		return containsUI;
	}

	/**
	 * @return Java class associated with the UI dialog of the pattern
	 */
	public Class<?> getUiClass() {
		return uiClass;
	}

	/**
	 * Set the UI class associated with the pattern
	 * 
	 * @param uiClass
	 *            UI class associated with the pattern
	 */
	public void setUiClass(final Class<?> uiClass) {
		containsUI = true;
		this.uiClass = uiClass;
	}

	/**
	 * Show the UI associated with the pattern
	 * 
	 * @param frame
	 *            Parent frame of the application
	 * @param schema
	 *            Schema over which the pattern is applied
	 * @param construct
	 *            Construct(s) that is(are) transformed
	 * @param beforeLabel
	 *            Before label
	 * @param afterLabel
	 *            After label
	 * @return Transformation result object
	 */
	public ITransformationResult showUI(final IMainFrame frame, final Schema schema,
			final ISqlConstruct[] construct, final JLabel beforeLabel, final JLabel afterLabel)
			throws IllegalArgumentException, InstantiationException, IllegalAccessException,
			InvocationTargetException, SecurityException, NoSuchMethodException {
		ITransformationResult result = null;
		if (uiClass != null) {
			final Constructor<?> constructor = uiClass.getConstructor(IMainFrame.class,
					Schema.class, ISqlConstruct[].class, JLabel.class, JLabel.class);
			final ITransformationDialog dialog = (ITransformationDialog) constructor.newInstance(
					frame, schema, construct, beforeLabel, afterLabel);
			dialog.setVisible(true);
			result = dialog.getResult();
		}

		return result;
	}

	@Override
	public String toString() {
		final StringBuilder builder = new StringBuilder();
		builder.append("Name = " + name + "; Operation = " + operation.toString()
				+ "; Description = " + description + "; Command = " + command + "; Has UI = "
				+ containsUI);

		return builder.toString();
	}

}
